/*
 * Copyright (c) 2018.
 * http://www.ulabcare.com
 */

package com.mt.roll.passport.controller;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.mt.roll.core.api.Assert;
import com.mt.roll.core.base.BaseController;
import com.mt.roll.core.constants.CommonConstant;
import com.mt.roll.core.constants.RedisConstant;
import com.mt.roll.core.dto.ApiLogDto;
import com.mt.roll.core.dto.LoginLogDto;
import com.mt.roll.core.dto.LoginUser;
import com.mt.roll.core.dto.Permission;
import com.mt.roll.core.errorcode.ApiErrorCode;
import com.mt.roll.core.service.RedisService;
import com.mt.roll.core.vo.R;
import com.mt.roll.core.vo.TokenVo;
import com.mt.roll.passport.config.PassportConfig;
import com.mt.roll.passport.feign.PlatformUpmsService;
import com.mt.roll.passport.params.LoginParam;
import com.mt.roll.passport.params.MobileLoginParam;
import com.mt.roll.passport.service.LogService;
import com.mt.roll.passport.service.PassportService;
import com.mt.roll.passport.service.SessionService;
import com.mt.roll.passport.validator.groups.PasswordLoginGroup;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.util.AntPathMatcher;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import javax.validation.groups.Default;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author jiff
 * @date 2018/11/1
 * @since 1.0
 */
@RestController
@RequestMapping(CommonConstant.API_MODULE_PASSPORT)
@Slf4j
@Validated
@Api(value = "统一用户接口", tags = "封装用户认证鉴权相关接口")
public class PassportController extends BaseController {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();


    @Autowired
    private PlatformUpmsService platformUpmsService;
    @Autowired
    private LogService logService;

    @Autowired
    private SessionService sessionService;

    @Autowired
    private PassportService passportService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private FindByIndexNameSessionRepository<? extends Session> sessionRepository;

    @Autowired
    private PassportConfig passportConfig;

    @PostMapping("login")
    @ApiOperation(value = "用户名密码登录", notes = "返回会话令牌")
    public R<TokenVo> login(@Validated({Default.class, PasswordLoginGroup.class}) @RequestBody LoginParam loginParam) {
        if (log.isDebugEnabled()) {
            log.debug("登录请求消息：{}", loginParam);
        }
        String mobile = loginParam.getMobile();
        String password = loginParam.getPassword();
        R<LoginUser> r = null;
        switch (loginParam.getUserType()) {
            case LoginUser.USER_TYPE_PLATFORM:
                r = platformUpmsService.login(mobile, password);
                break;
            default:
                break;
        }
        if (r != null && r.ok()) {
            return loginSuccessHandler(r.getData(), loginParam);
        }
        return failed(r == null ? "登录失败，用户名或密码错误！" : r.getMsg());
    }


    @PostMapping("mobileLogin")
    @ApiOperation(value = "短信验证码登录", notes = "返回会话令牌")
    public R<TokenVo> login(@Validated @RequestBody MobileLoginParam loginParam) {
        if (log.isDebugEnabled()) {
            log.debug("登录请求消息：{}", loginParam);
        }
        String mobile = loginParam.getMobile();
        String smsCode = loginParam.getSmsCode();
        String validSmsCode = redisService.get(RedisConstant.genSmsCodeKey(mobile));
        if (StringUtils.isNoneBlank(smsCode, validSmsCode) && smsCode.equals(validSmsCode)) {
            redisService.del(RedisConstant.genSmsCodeKey(mobile));
            R<LoginUser> r = null;
            switch (loginParam.getUserType()) {
                case LoginUser.USER_TYPE_PLATFORM:
                    r = platformUpmsService.getLoginUser(mobile);
                    break;
                default:
                    break;
            }
            if (r != null && r.ok()) {
                LoginUser loginUser = r.getData();
                //更新手机号码激活状态

                LoginParam loginParam1 = LoginParam.builder().build();
                BeanUtils.copyProperties(loginParam, loginParam1);
                return loginSuccessHandler(loginUser, loginParam1);
            } else {
                return failed(r == null ? "登录失败，手机号码或验证码错误！" : r.getMsg());
            }
        }
        return failed("登录失败，手机号码或验证码错误！");
    }


    private R<TokenVo> loginSuccessHandler(LoginUser loginUser, LoginParam loginParam) {
        String unionId = null;
        String openId = null;
        String wechatAppId = loginParam.getWechatAppId();
        String oauthCode = loginParam.getOauthCode();
        int appType = loginParam.getAppType();
        Long passportUserId = loginUser.getPassportUserId();


        //当前登录用户信息存入session中
        HttpSession session = getHttpServletRequest().getSession();
        loginUser.setAppType(loginParam.getAppType());
        loginUser.setLoginType(LoginLogDto.LOGIN_TYPE_SELF);
        loginUser.setWechatAppId(wechatAppId);
        loginUser.setOpenId(openId);
        session.setAttribute(LoginUser.HTTP_HEADER_NAME, JSON.toJSONString(loginUser));
        session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, loginUser.getMobile());
        //保存登录日志
        passportService.saveLoginLog(LoginLogDto.builder()
                .sessionId(session.getId())
                .passportUserId(loginUser.getPassportUserId())
                .userId(loginUser.getUserId())
                .orgId(loginUser.getOrgId())
                .mobile(loginUser.getMobile())
                .userName(loginUser.getUserName())
                .userType(loginUser.getUserType())
                .appType(loginUser.getAppType())
                .appCode(loginUser.getAppCode())
                .ip(HttpUtil.getClientIP(getHttpServletRequest()))
                .userAgent(getHttpServletRequest().getHeader(HttpHeaders.USER_AGENT))
                .loginType(loginUser.getLoginType())
                .loginTime(new Date())
                .action(LoginLogDto.ACTION_LOGIN)
                .build());
        if (log.isDebugEnabled()) {
            log.debug("登录成功：sessionId={}", session.getId());
        }
        return R.ok("登录成功", TokenVo.builder().token(session.getId()).build());
    }


    @PostMapping(value = "getLoginUser")
    @ApiOperation(value = "获取登录用户", notes = "返回登录用户信息")
    public R<LoginUser> getLoginUserVo() {
        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            return R.unauthorized();
        }
        return successful(loginUser);
    }

    @PostMapping("authorization")
    @ApiOperation(value = "用户鉴权", notes = "返回登录用户信息", hidden = true)
    public R<LoginUser> authorization(@RequestParam String url, @RequestParam String method) {
        if (log.isDebugEnabled()) {
            log.debug("用户鉴权，url={},method={}", url, method);
        }
        Assert.notNull(ApiErrorCode.FAILED, url, method);
        HttpSession session = getHttpServletRequest().getSession(false);
        LoginUser loginUser = null;
        if (session != null) {
            String loginUserJson = (String) session.getAttribute(LoginUser.HTTP_HEADER_NAME);
            if (StringUtils.isNotBlank(loginUserJson)) {
                loginUser = JSON.parseObject(loginUserJson, LoginUser.class);
            }
        }
        //如果忽略认证，则直接返回成功
        if (isIgnoreAuthentication(url)) {
            //登录用户信息为空保存接口调用日志
            if (loginUser != null) {
                passportService.saveApiLog(ApiLogDto.builder()
                        .sessionId(session.getId())
                        .passportUserId(loginUser.getPassportUserId())
                        .userId(loginUser.getUserId())
                        .userName(loginUser.getUserName())
                        .userType(loginUser.getUserType())
                        .ip(HttpUtil.getClientIP(getHttpServletRequest()))
                        .userAgent(getHttpServletRequest().getHeader(HttpHeaders.USER_AGENT))
                        .url(url).method(method)
                        .time(new Date())
                        .build());
            }
            return successful(loginUser);
        } else if (loginUser == null) {
            return R.unauthorized();
        }

        boolean hasPermission = false;
        //忽略鉴权
        if (isIgnoreAuthorization(url)) {
            hasPermission = true;
        } else {
            List<Permission> permissions;
            switch (loginUser.getUserType()) {
                case LoginUser.USER_TYPE_PLATFORM: {
                    permissions = platformUpmsService.getPermissions();
                    break;
                }
                default:
                    permissions = new ArrayList<>();
                    break;
            }
            if (permissions != null) {
                for (Permission permission : permissions) {
                    if (StringUtils.isNotEmpty(permission.getUrl()) && antPathMatcher.match(permission.getUrl(), url)
                            && method.equalsIgnoreCase(permission.getMethod())) {
                        hasPermission = true;
                        break;
                    }
                }
            }
        }
        passportService.saveApiLog(ApiLogDto.builder()
                .sessionId(session.getId())
                .passportUserId(loginUser.getPassportUserId())
                .userId(loginUser.getUserId())
                .userName(loginUser.getUserName())
                .userType(loginUser.getUserType())
                .ip(HttpUtil.getClientIP(getHttpServletRequest()))
                .userAgent(getHttpServletRequest().getHeader(HttpHeaders.USER_AGENT))
                .url(url).method(method)
                .time(new Date())
                .build());
        if (hasPermission) {
            return R.ok("鉴权成功", loginUser);
        } else {
            return R.forbidden();
        }
    }

    /**
     * 是否忽略认证
     *
     * @param url 接口地址
     * @return
     */
    private boolean isIgnoreAuthentication(String url) {
        for (String ignore : passportConfig.getIgnoreAuthentications()) {
            if (log.isDebugEnabled()) {
                log.debug("ignore authentication url is {}", ignore);
            }
            if (antPathMatcher.match(ignore, url)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否忽略鉴权
     *
     * @param url 接口地址
     * @return
     */
    private boolean isIgnoreAuthorization(String url) {
        for (String ignore : passportConfig.getIgnoreAuthorizations()) {
            if (log.isDebugEnabled()) {
                log.debug("ignore authorization url is {}", ignore);
            }
            if (antPathMatcher.match(ignore, url)) {
                return true;
            }
        }
        return false;
    }
}
